package com

import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.file.Path
import kotlin.io.path.readBytes

object Crc32 {

    private val crcTable: Array<Int> by lazy {
        val table = Array(256) { 0 }
        var crc: Int
        table.forEachIndexed { index, _ ->
            crc = index
            for (i in 0..<8) {
                crc = if (crc and 1 == 1) crc ushr 1 xor -0x12477ce0 else crc ushr 1
            }
            table[index] = crc ushr 0
        }
        table
    }

    /**
     * 将字符串生成crc值
     */
    fun crc32(str: String): Int {
        val utf8CharCodes = utf8encode(str)
        var crc = -1
        for (element in utf8CharCodes) {
            val y = crc xor element and 0xFF
            crc = crc ushr 8 xor crcTable[y]
        }
        return crc xor -1 ushr 0
    }

    private fun utf8encode(str: String): List<Int> {
        val utf8CharCodes = mutableListOf<Int>()
        for (i in str) {
            val c = i.code
            if (c < 128) {
                utf8CharCodes.add(c)
            } else if (c < 2048) {
                utf8CharCodes.add(c shr 6 or 192)
                utf8CharCodes.add(c and 63 or 128)
            } else {
                utf8CharCodes.add(c shr 12 or 224)
                utf8CharCodes.add(c shr 6 and 63 or 128)
                utf8CharCodes.add(c and 63 or 128)
            }
        }
        return utf8CharCodes
    }

    /**
     * //        System.out.println(Integer.toHexString(240089644));
     * //        int re = new BigInteger("e4f7a2c", 16).intValue();
     * //        System.out.println(re);
     */

    fun crc32(path: Path): Int {
        return crc32(path.readBytes())
    }

    /**
     * 将二进制数据生成crc值
     */
    fun crc32(buff: ByteArray): Int {
        val len = buff.size
        //计算简单校验
        val checkbuff = ByteArray(8)
        val clen = len - len % 8 //最后剩下的算法不同，所以要分开
        for (i in 0 until clen) {
            checkbuff[i % 8] = (checkbuff[i % 8].toInt() xor buff[i].toInt()).toByte()
        }
        for (i in clen until len) {
            checkbuff[0] = (checkbuff[0].toInt() xor buff[i].toInt()).toByte()
        }
        val checksum = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
        checksum.put((checkbuff[0].toInt() xor checkbuff[4].toInt()).toByte())
        checksum.put((checkbuff[1].toInt() xor checkbuff[5].toInt()).toByte())
        checksum.put((checkbuff[2].toInt() xor checkbuff[6].toInt()).toByte())
        checksum.put((checkbuff[3].toInt() xor checkbuff[7].toInt()).toByte())
        var crc = checksum.getInt(0)
        //        int crc = checksum.readUInt32LE(0);
        //由于这个算法太简单，可能会产生出0来，而0会被检验函数认为错误，所以要处理一下
        if (crc == 0) crc = 1
        return crc
    }

}